package com.yough.api.framework.core.parse.helper;

import com.you.api.common.util.ApiModelProperty;
import com.you.api.common.util.ApiOperation;
import com.yough.api.framework.bean.ApiDocumentBean;
import com.yough.api.framework.bean.DocumentBeanDefinition;
import com.yough.api.framework.bean.reflect.MethodParamType;
import com.yough.api.framework.bean.reflect.MethodResultType;
import com.yough.api.framework.constant.DocumentConstants;
import com.yough.api.framework.core.parse.DocumentParserHelper;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @Description jar包解析辅助类
 * @Author yough
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2021/1/7
 */
public class JarDocumentParserHelper extends DocumentParserHelper {

    @Override
    public void doLoadDocument(String jarFile) {
        DocumentBeanDefinition documentBeanDefinition;
        try {
            File file = new File(jarFile);
            URL url = file.toURI().toURL();
            // 类加载器
            URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader());
            JarFile jar = new JarFile(jarFile);
            Enumeration<JarEntry> enumFiles = jar.entries();
            JarEntry entry;
            while (enumFiles.hasMoreElements()) {
                entry = (JarEntry) enumFiles.nextElement();
                // 排除jar包META-INF目录的扫描和解析
                if (entry.getName().indexOf("META-INF") < 0) {
                    String classFullName = entry.getName();
                    if (classFullName.indexOf(".class") < 0) {
                        classFullName = classFullName.substring(0, classFullName.length() - 1);
                    } else {
                        /**
                         * 加载class类
                         */
                        String className = classFullName.substring(0, classFullName.length() - 6).replace("/", ".");
                        Class<?> clazz = urlClassLoader.loadClass(className);
                        //判断是否接口类，指定解析接口类
                        if (clazz.isInterface()) {
                            documentBeanDefinition = new DocumentBeanDefinition();
                            documentBeanDefinition.setInterface(true);
                            String key = this.getSysNo() + className;
                            documentBeanDefinition.setSysNo(this.getSysNo());
                            documentBeanDefinition.setClazz(clazz);
                            /**
                             * 类的成员变量
                             */
                            documentBeanDefinition.setFileds(clazz.getDeclaredFields());
                            /**
                             * 类的方法列表
                             */
                            documentBeanDefinition.setMethods(clazz.getMethods());
                            //存放容器
                            DocumentConstants.documentBeanDefinitionMap.put(key, documentBeanDefinition);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doParseToApiDocument() {
        ConcurrentHashMap<String, DocumentBeanDefinition> documentBeanDefinitionMap =
                DocumentConstants.documentBeanDefinitionMap;
        ConcurrentHashMap<String, ApiDocumentBean> apiDocumentBeanMap = DocumentConstants.apiDocumentBeanMap;
        ConcurrentHashMap.KeySetView<String, DocumentBeanDefinition> mapKeySet = documentBeanDefinitionMap.keySet();
        Iterator<String> keyIterator = mapKeySet.iterator();
        while (keyIterator.hasNext()) {
            String key = keyIterator.next();
            DocumentBeanDefinition documentBeanDefinition = documentBeanDefinitionMap.get(key);
            // 成员变量
//            Field[] fileds = documentBeanDefinition.getFileds();
//            for (Field field : fileds) {
//                System.out.println("成员变量修饰符：" + field.getModifiers());
//                System.out.println("成员变量名称：" + field.getName());
//                System.out.println("成员变量声明类型：" + field.getDeclaringClass());
//                System.out.println("成员变量的注解集合：" + field.getAnnotations());
//                //System.out.println("成员变量的注解类型：" + field.getAnnotatedType());
//            }
            // 类的方法列表
            Method[] methods = documentBeanDefinition.getMethods();
            // 生成的api document
            ApiDocumentBean apiDocumentBean = null;
            for (Method method : methods) {
                apiDocumentBean = new ApiDocumentBean();
                // 解析方法
                this.doParseMethod(method, apiDocumentBean);
                // 加入到apiDocumentBeanMap中
                String SysNoKey = this.getSysNo() + method.getName();
                apiDocumentBeanMap.put(SysNoKey, apiDocumentBean);
            }
        }
    }

    /**
     * 解析方法的公共信息
     *
     * @param method
     * @param apiDocumentBean
     */
    private void doParseMethod(Method method, ApiDocumentBean apiDocumentBean) {
        // 是否有ApiOperation注解
        if (method.isAnnotationPresent(ApiOperation.class)) {
            // 获取ApiOperation注解的值
            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
            apiDocumentBean.setInterfaceName(apiOperation.value());
            apiDocumentBean.setInterfaceDesc(apiOperation.desc());
            apiDocumentBean.setRequestUrl(method.getName());
            apiDocumentBean.setRequestType("GET,POST");
            // 参数列表
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 解析方法的请求参数
            this.doParseMethodRequestParam(parameterTypes, apiDocumentBean);
            //  解析方法的响应参数
            this.doParseMethodResponseResult(method.getReturnType(), apiDocumentBean, apiOperation);


        }
    }

    /**
     * 解析方法的请求参数
     *
     * @param parameterTypes
     * @param apiDocumentBean
     */
    private void doParseMethodRequestParam(Class<?>[] parameterTypes, ApiDocumentBean apiDocumentBean) {
        List<MethodParamType> requestParams = new ArrayList<MethodParamType>();
        int count = 0;
        // 解析所有的参数
        for (Class<?> paramTypeClass : parameterTypes) {
            count++;
            // 参数类型名称
//            System.out.println("--------参数名: -----" + paramTypeClass.getName());
//            System.out.println("--------参数类型全限定名称: -----" + paramTypeClass.getTypeName());
//            System.out.println("--------参数类型简单名称：-----" + paramTypeClass.getSimpleName());
//            System.out.println("--------参数类型：-----" + paramTypeClass.getClass());
            // 判断是否java lang基本数据类型
            if (DocumentConstants.javaLangType.contains(paramTypeClass.getSimpleName())) {
                // 基本数据类型，直接加入到paramTypes中
                MethodParamType<String> paramType = new MethodParamType();
                paramType.setApiParamType(paramTypeClass.getSimpleName());
                paramType.setApiParamName("入参" + count);
                paramType.setApiParamDefaultValue("");
                paramType.setApiParamDesc("");
                paramType.setApiParamIsRequired(false);
                // 加入到参数列表里
                requestParams.add(paramType);
            } else {
                // 解析成员变量
                this.doParseRequestParamFieldProperty(paramTypeClass, requestParams);
                apiDocumentBean.setMethodParamClassType(paramTypeClass);
            }
        }
        apiDocumentBean.setRequestParams(requestParams);
    }

    /**
     * 解析成员变量
     *
     * @param paramClass
     * @param requestParams
     */
    private void doParseRequestParamFieldProperty(Class<?> paramClass, List<MethodParamType> requestParams) {
        // 获取该类的成员变量
        Field[] memberFields = paramClass.getDeclaredFields();
        // 解析成员变量上的注解
        for (Field field : memberFields) {
//            System.out.println("字段名称：" + field.getName() + "--" + field.getType() +
//                    "," + field.getType().getSimpleName() + "," + field.getDeclaringClass());
//            System.out.println("字段是否注解" + field.isAnnotationPresent(ApiModelProperty.class));
            // 如果成员变量上有ApiModelProperty注解
            if (field.isAnnotationPresent(ApiModelProperty.class)) {
                // 判断是否java lang基本数据类型
                if (DocumentConstants.javaLangType.contains(field.getType().getSimpleName())) {
                    // 如果是基本类型
                    ApiModelProperty memberAnnotation = field.getAnnotation(ApiModelProperty.class);
                    // 判断是否java lang基本数据类型，基本数据类型，直接加入到paramTypes中
                    MethodParamType<String> paramType = new MethodParamType();
                    paramType.setApiParamType(field.getType().getSimpleName());
                    paramType.setApiParamName(field.getName());
                    paramType.setApiParamDefaultValue(memberAnnotation.name());
                    paramType.setApiParamDesc(memberAnnotation.value());
                    paramType.setApiParamIsRequired(memberAnnotation.required());
                    // 加入到参数列表里
                    requestParams.add(paramType);
                } else {
                    // 递归解析，直到所有类型为Java基本数据类型
//                    System.out.println("-----" + field.getDeclaringClass());
                    doParseRequestParamFieldProperty(field.getType(), requestParams);
                }
            }
        }
    }

    /**
     * 解析方法的返回参数
     *
     * @param returnType
     * @param apiDocumentBean
     * @param apiOperation
     */
    private void doParseMethodResponseResult(Class<?> returnType, ApiDocumentBean apiDocumentBean, ApiOperation apiOperation) {
        List<MethodResultType> responseTypes = new ArrayList<>();
        String returnTypeName = returnType.getName();
        //System.out.println("返回类型:" + returnTypeName);
        // 判断是否java lang基本数据类型
        if (DocumentConstants.javaLangType.contains(returnType.getSimpleName())) {
            // 基本数据类型，直接加入到paramTypes中
            MethodResultType<String> resultType = new MethodResultType();
            resultType.setApiResultName(apiOperation.resultParamName() == null ?
                    returnType.getSimpleName() : apiOperation.resultParamName());
            resultType.setApiResultType(returnType.getSimpleName());
            resultType.setApiResultDesc(apiOperation.resultDesc() == null ?
                    "" : apiOperation.resultDesc());
            resultType.setApiResultIsRequired(false);
            // 加入到参数列表里
            responseTypes.add(resultType);
        } else {
            // 解析成员变量
            this.doParseResultParamFieldProperty(returnType, responseTypes);
            apiDocumentBean.setMethodResultClassType(returnType);
        }
        apiDocumentBean.setResponseTypes(responseTypes);
    }

    /**
     * 解析成员变量
     *
     * @param paramClass
     * @param responseTypes
     */
    private void doParseResultParamFieldProperty(Class<?> paramClass, List<MethodResultType> responseTypes) {
        // 获取该类的成员变量
        Field[] memberFields = paramClass.getDeclaredFields();
        // 解析成员变量上的注解
        for (Field field : memberFields) {
//            System.out.println("字段名称：" + field.getName() + "--" + field.getType() +
//                    "," + field.getType().getSimpleName() + "," + field.getDeclaringClass());
//            System.out.println("字段是否注解" + field.isAnnotationPresent(ApiModelProperty.class));
            // 如果成员变量上有ApiModelProperty注解
            if (field.isAnnotationPresent(ApiModelProperty.class)) {
                // 判断是否java lang基本数据类型
                if (DocumentConstants.javaLangType.contains(field.getType().getSimpleName())) {
                    // 如果是基本类型
                    ApiModelProperty memberAnnotation = field.getAnnotation(ApiModelProperty.class);
                    // 判断是否java lang基本数据类型，基本数据类型，直接加入到paramTypes中
                    MethodResultType<String> resultType = new MethodResultType();
                    resultType.setApiResultType(field.getType().getSimpleName());
                    resultType.setApiResultName(field.getName());
                    resultType.setApiResultDesc(memberAnnotation.value());
                    // 加入到参数列表里
                    responseTypes.add(resultType);
                } else {
                    // 递归解析，直到所有类型为Java基本数据类型
                    doParseResultParamFieldProperty(field.getType(), responseTypes);
                }
            }
        }
    }
}
